﻿Imports System.Text
Imports System.ComponentModel
Imports System.Threading
Imports System.Runtime.InteropServices

Public Class RelayPort : Inherits Transport

#Region "Frame ID"
    Public Const ID_SET_TIME As Byte = &H3
    Public Const ID_SET_TASK_TIME As Byte = &H4
    Public Const ID_SET_EACH_RELAY As Byte = &H5
    Public Const ID_GET_SET_TIME As Byte = &H6
    Public Const ID_SET_BLE_NAME As Byte = &H7
    Public Const ID_GET_BLE_NAME As Byte = &H8
    Public Const ID_GET_EACH_RELAY As Byte = &H9
    Public Const ID_SET_MULTI_TASK As Byte = &HA
    Public Const ID_SET_PERIOD_TASK As Byte = &HB
    Public Const ID_SET_DEVICE_ID As Byte = &HC
#End Region

    Public Const MaxRelay As Integer = 6
    Public FrameGetSetTime As Byte() = {&H68, &H6, &H0, &HC, &HE4, &HDF, &HEF, &H7E}                '获取设置时间
    Public FrameGetBLEName As Byte() = {&H68, &H8, &H0, &H82, &HC9, &H5C, &H71, &H7E}               '获取蓝牙名（前提已经设置过，否则会回复全0）
    Public FrameGetEachRelay As Byte() = {&H68, &H9, &H0, &HC3, &HF8, &H47, &H68, &H7E}            '获取每个继电器状态
    Public FrameGetDeviceId As Byte() = {&H68, &HC, &HD, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H22, &H80, &HE6, &H54, &H7E}
    Public ControlTask As SetControlTask_T              '定时任务
    Public ControlEachRelay As SetEachRelay_T
    Public EachRelayState As GetEachRelay_T
    Public DeviceTime As SetDeviceTime_T                '设备时间
    Public DeviceName As SetBleName_T
    Public MultiTask As MultiTask_T
    Public PeriodTask As PeriodTask_T
    Public DeviceId As String
    Public BleName As String

    Delegate Sub ParseHandler(frame As Byte())
    Public MsgHandlers(16) As ParseHandler

    Public Sub New()
        ReDim ControlTask.Relay(MaxRelay - 1)
        ReDim ControlEachRelay.Relays(MaxRelay - 1)
        ReDim EachRelayState.Relays(MaxRelay - 1)
        For i As Integer = 0 To MaxRelay - 1
            ReDim ControlTask.Relay(i).StartTime(2)
            ReDim ControlTask.Relay(i).StopTime(2)
        Next
        MsgHandlers(ID_GET_EACH_RELAY) = AddressOf ParseRelayState
        MsgHandlers(ID_GET_SET_TIME) = AddressOf ParseDeviceTime
        MsgHandlers(ID_GET_BLE_NAME) = AddressOf ParseDeviceName
        MsgHandlers(ID_SET_DEVICE_ID) = AddressOf ParseDeviceId
    End Sub

    Public Function GetSetTime() As Boolean
        Dim ret As Boolean = True
        If Me.Ready Then
            port.Write(FrameGetSetTime, 0, FrameGetSetTime.Length)
            ret = Parse(ReadAck())
        End If
        Return ret
    End Function

    Public Function GetBleName() As String
        If Me.Ready Then
            port.Write(FrameGetBLEName, 0, FrameGetBLEName.Length)
            Parse(ReadAck())
        End If
        Return BleName
    End Function

    Public Function GetDeviceId() As String
        If Ready Then
            port.Write(FrameGetDeviceId, 0, FrameGetDeviceId.Length)
            Parse(ReadAck())
        End If
        Return DeviceId
    End Function

    Public Sub GetEachRealy()
        If Me.Ready Then
            port.Write(FrameGetEachRelay, 0, FrameGetEachRelay.Length)
            Parse(ReadAck())
        End If
    End Sub

    ''' <summary>
    ''' 解析继电器状态
    ''' </summary>
    ''' <param name="frame">带帧头的协议应答</param>
    Public Sub ParseRelayState(frame As Byte())
        For i As Integer = 0 To MaxRelay - 1
            EachRelayState.Relays(i) = frame(i + 3)
        Next
    End Sub

    Public Sub ParseDeviceId(frame As Byte())
        Dim id(11) As Byte
        Dim i As Integer
        For i = 0 To id.Length - 1
            id(i) = frame(4 + i)
        Next
        Try
            DeviceId = Encoding.ASCII.GetString(id)
        Catch ex As Exception
            Console.WriteLine($"[Error] {ex.Message}")
        End Try
    End Sub

    Public Sub ParseDeviceName(frame As Byte())
        Dim name(11) As Byte
        Dim i As Integer

        For i = 0 To name.Length - 1
            name(i) = frame(3 + i)
            If name(i) = 0 Then
                Exit For
            End If
        Next

        If i = 0 Then
            BleName = ""
        Else
            ReDim Preserve name(i - 1)
            Try
                BleName = Encoding.ASCII.GetString(name)
            Catch ex As Exception
                Console.WriteLine($"[Error] {ex.Message}")
            End Try
        End If
    End Sub

    Public Sub ParseDeviceTime(frame As Byte())
        Dim device_time(5) As Byte
        Dim set_time(35) As Byte
        Dim i As Integer
        For i = 0 To device_time.Length - 1
            device_time(i) = frame(i + 3)
        Next

        For i = 0 To set_time.Length - 1
            set_time(i) = frame(i + 9)
        Next

        DeviceTime = Transport.ByteToStruct(device_time, DeviceTime.GetType())
        ControlTask = Transport.ByteToStruct(set_time, ControlTask.GetType())
    End Sub

    ''' <summary>
    ''' 设置继电器状态
    ''' </summary>
    ''' <param name="index">继电器编号 1~6 </param>
    ''' <param name="val">on or off</param>
    Public Sub SetEachRelay(index As Integer, val As Boolean)
        ReDim ControlEachRelay.Relays(MaxRelay - 1) '清状态，防止历史遗留数据
        If val Then ControlEachRelay.Relays(index - 1).SetOn = 1 Else ControlEachRelay.Relays(index - 1).SetOff = 1
        SendMsg(GenerateMsg(ID_SET_EACH_RELAY, ControlEachRelay))
        Parse(ReadAck())
    End Sub

    Public Function SetBleName(name As Byte()) As Boolean
        Dim ret As Boolean = True
        If name IsNot Nothing Then
            If name.Length > 0 Then
                ReDim DeviceName.Name(name.Length - 1)
                name.CopyTo(DeviceName.Name, 0)
                SendMsg(GenerateMsg(ID_SET_BLE_NAME, DeviceName))
                ret = Parse(ReadAck())
            End If
        End If
        Return ret
    End Function

    Public Function SetDeviceId(id As Byte()) As Boolean
        Dim ret As Boolean = True
        Dim device_info As SetDeviceID_T
        ReDim device_info.Id(12)
        'ID号必须是12位
        If id.Length <> 12 Then
            ret = False
        Else
            id.CopyTo(device_info.Id, 0)
            device_info.RW = 1 '写
            SendMsg(GenerateMsg(ID_SET_DEVICE_ID, device_info))
            Parse(ReadAck())
        End If
        Return ret
    End Function

    Public Sub SetDeviceTime(s As DateTime)
        DeviceTime.Hour = s.Hour
        DeviceTime.Year = s.Year Mod 100
        DeviceTime.Month = s.Month
        DeviceTime.Day = s.Day
        DeviceTime.Minute = s.Minute
        DeviceTime.Second = s.Second
        SendMsg(GenerateMsg(ID_SET_TIME, DeviceTime))
        Parse(ReadAck())
    End Sub

    ''' <summary>
    ''' 设定周期任务
    ''' </summary>
    ''' <param name="index">继电器编号</param>
    ''' <param name="start_time">打开时间</param>
    ''' <param name="stop_time">关闭时间</param>
    Public Sub SetControlTask(index As Integer, start_time As DateTime, stop_time As DateTime)
        ' 不设置的继电器时间设置为0xFF，在下位机忽略0xFF
        For i As Integer = 0 To MaxRelay - 1
            ControlTask.Relay(i).StartTime = New Byte(2) {&HFF, &HFF, &HFF}
            ControlTask.Relay(i).StopTime = New Byte(2) {&HFF, &HFF, &HFF}
        Next
        ControlTask.Relay(index - 1).StartTime(0) = start_time.Hour
        ControlTask.Relay(index - 1).StartTime(1) = start_time.Minute
        ControlTask.Relay(index - 1).StartTime(2) = start_time.Second
        ControlTask.Relay(index - 1).StopTime(0) = stop_time.Hour
        ControlTask.Relay(index - 1).StopTime(1) = stop_time.Minute
        ControlTask.Relay(index - 1).StopTime(2) = stop_time.Second

        SendMsg(GenerateMsg(ID_SET_TASK_TIME, ControlTask))
        Parse(ReadAck())
    End Sub

    ''' <summary>
    ''' 像设备发送多段时间任务
    ''' </summary>
    ''' <param name="multi_task"></param>
    Public Sub SetMultiTasks(multi_task As MultiTask_T)
        SendMsg(GenerateMsg(ID_SET_MULTI_TASK, multi_task))
        Parse(ReadAck())
    End Sub

    ''' <summary>
    ''' 设置周期任务
    ''' </summary>
    ''' <param name="index">继电器编号</param>
    ''' <param name="period">定时周期</param>
    Public Sub SetPeriodTask(index As Integer, period As UInteger)
        ReDim PeriodTask.Periods(5)
        PeriodTask.Periods(index - 1) = period
        SendMsg(GenerateMsg(ID_SET_PERIOD_TASK, PeriodTask))
        Parse(ReadAck())
    End Sub

    Public Function Parse(frame As Byte()) As Boolean

        Dim ret As Boolean = True

        Dim print_byte As Action(Of Byte()) = Sub(bs As Byte())
                                                  For Each b As Byte In bs
                                                      Console.Write(b.ToString("X2") + " ")
                                                  Next
                                                  Console.Write(vbCrLf)
                                              End Sub

        If frame IsNot Nothing Then
            If frame.Length > 0 Then
                print_byte(frame)
                If frame(frame.Length - 1) = &H7E Then
                    Dim crc_check As UInteger = CRC32.Cal(frame, frame.Length - 5)

                    Dim crc_index = frame.Length - 5
                    Dim crc_bytes = New Byte() {frame(crc_index), frame(crc_index + 1), frame(crc_index + 2), frame(crc_index + 3)}
                    Dim crc_get As UInteger = BitConverter.ToUInt32(crc_bytes, 0)

                    If (crc_get <> crc_check) Then
                        Console.WriteLine("CRC Error.")
                        ret = False
                    Else
                        If (MsgHandlers(frame(1)) IsNot Nothing) Then
                            MsgHandlers(frame(1)).Invoke(frame)
                        End If
                    End If
                ElseIf frame(0) = Encoding.ASCII.GetBytes("B")(0) Then
                    'ReDim Preserve frame(frame.Length)
                    Console.WriteLine(Encoding.ASCII.GetString(frame))
                Else
                    Console.WriteLine("Error Tail.")
                    ret = False
                End If
            End If
        Else
            Console.WriteLine("Empty Message.")
            ret = False
        End If

        Return ret

    End Function

    Public Function ReadAck() As Byte()
        Dim data() As Byte = Nothing
        Console.WriteLine("------------ wait ack ------------")
        Dim start_time As Long = DateTime.Now.Ticks()
        Dim len As Integer = 0
        Try
            '1ticks = 100纳秒 = 0.1微秒
            While DateTime.Now.Ticks() - start_time < 10000 * 200
                If len = 0 Then
                    If port.BytesToRead >= 3 Then
                        Dim head(2) As Byte
                        port.Read(head, 0, head.Length)
                        If head(0) = &H68 Then
                            len = head(2) + 5 '4位crc和1位结束符
                            ReDim data(len + 2)
                            head.CopyTo(data, 0)
                        ElseIf head(0) = Encoding.ASCII.GetBytes("B")(0) Then
                            'BT开头的指令
                            len = 4 '还有4字节要读
                            ReDim data(len + 2)
                            head.CopyTo(data, 0)
                        Else
                            If head(0) <> Encoding.ASCII.GetBytes("A")(0) Then Console.WriteLine("unkown head.")
                            'AT开头的指令
                            Thread.Sleep(100)
                            ReDim Preserve head(2 + port.BytesToRead)
                            port.Read(head, 3, port.BytesToRead)
                            Console.WriteLine(Encoding.ASCII.GetString(head))
                            Return Nothing
                        End If

                    End If
                Else
                    If port.BytesToRead >= len Then
                        port.Read(data, 3, len)
                        port.DiscardInBuffer()
                    End If
                End If
            End While
        Catch ex As Exception
            Console.WriteLine($"[Error] {ex.Message}")
        End Try
        'Console.WriteLine(ret)
        Return data
    End Function

    ''' <summary>
    ''' 生成要发送的协议
    ''' </summary>
    ''' <param name="msg_id">信息类型</param>
    ''' <param name="data_struct">数据结构体</param>
    ''' <returns></returns>
    Public Function GenerateMsg(msg_id As Byte, data_struct As Object) As Byte()
        Dim tx_buffer(4) As Byte
        Dim data_buf() As Byte = Nothing
        Dim data_len As Integer = 0
        tx_buffer(0) = &H68

        If msg_id = ID_SET_BLE_NAME Then
            data_len = DeviceName.Name.Length
            data_buf = DeviceName.Name
        Else
            data_len = Marshal.SizeOf(data_struct)
            data_buf = StructToByte(data_struct)
        End If

        If msg_id = ID_SET_MULTI_TASK Then
            '去掉多余的无用数据
            data_len = data_buf(1) * 6 + 2
            ReDim Preserve data_buf(data_len - 1)
        End If

        tx_buffer(2) = data_len
        ReDim Preserve tx_buffer(data_len + 7) 'head(1), msg_id(1), len(1), crc32(4), tail(1), vb.net是以最后索引的下标定义数组的

        tx_buffer(1) = msg_id

        If data_buf IsNot Nothing Then
            Array.Copy(data_buf, 0, tx_buffer, 3, data_buf.Length)
        End If
        '计算校验和CRC
        Dim crc32_byte(3) As Byte
        Dim crc As UInteger = CRC32.Cal(tx_buffer, tx_buffer(2) + 3)
        crc32_byte = BitConverter.GetBytes(crc)
        tx_buffer(tx_buffer.Length - 5) = crc32_byte(0)
        tx_buffer(tx_buffer.Length - 4) = crc32_byte(1)
        tx_buffer(tx_buffer.Length - 3) = crc32_byte(2)
        tx_buffer(tx_buffer.Length - 2) = crc32_byte(3)
        tx_buffer(tx_buffer.Length - 1) = &H7E
        Return tx_buffer
    End Function

    ''' <summary>
    ''' 下发协议
    ''' </summary>
    ''' <param name="buf">协议内容</param>
    ''' <returns></returns>
    Public Function SendMsg(buf() As Byte) As Boolean
        Console.WriteLine("------------ I send ------------")

        '发送缓存数据
        For Each b As Byte In buf
            Console.Write(b.ToString("X2") + " ")
        Next
        Console.Write(vbCrLf)

        Dim ret As Boolean = True
        If buf.Length < 5 Then
            ret = False
        Else
            If buf(0) <> &H68 Then
                ret = False
            Else
                Dim total_len As Integer = buf(2) + 8
                port.Write(buf, 0, total_len)
            End If
        End If
        Return ret
    End Function

End Class

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)>
Public Structure SetControlTask_T

    Public Structure ControlTask
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)>
        Public StartTime() As Byte
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)>
        Public StopTime() As Byte
    End Structure

    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=6)>
    Public Relay() As ControlTask

End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)>
Public Structure SetEachRelay_T
    Structure RelayOnOff
        Public SetOn As Byte
        Public SetOff As Byte
    End Structure

    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=6)>
    Public Relays() As RelayOnOff
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)>
Public Structure GetEachRelay_T
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=6)>
    Public Relays() As Byte
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)>
Public Structure SetDeviceTime_T
    Public Year As Byte
    Public Month As Byte
    Public Day As Byte
    Public Hour As Byte
    Public Minute As Byte
    Public Second As Byte
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)>
Public Structure SetBleName_T
    Public Name() As Byte
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)>
Public Structure MultiTask_T
    Public Structure ControlTask
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)>
        Public StartTime() As Byte
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)>
        Public StopTime() As Byte
    End Structure

    Public Index As Byte
    Public TaskAmount As Byte

    '这一段属性非常重要，表明这个数组会有多大，实际应用中应该设置为10段
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=5)>
    Public Tasks() As ControlTask

End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)>
Public Structure PeriodTask_T

    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=6)>
    Public Periods() As UInteger

End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)>
Public Structure SetDeviceID_T

    Public RW As Byte
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=12)>
    Public Id() As Byte

End Structure

Public Class CRC32

    Public Shared crc32tab() As UInteger = {
     &H0, &H77073096L, &HEE0E612CL, &H990951BAL,
     &H76DC419L, &H706AF48FL, &HE963A535L, &H9E6495A3L,
     &HEDB8832L, &H79DCB8A4L, &HE0D5E91EL, &H97D2D988L,
     &H9B64C2BL, &H7EB17CBDL, &HE7B82D07L, &H90BF1D91L,
     &H1DB71064L, &H6AB020F2L, &HF3B97148L, &H84BE41DEL,
     &H1ADAD47DL, &H6DDDE4EBL, &HF4D4B551L, &H83D385C7L,
     &H136C9856L, &H646BA8C0L, &HFD62F97AL, &H8A65C9ECL,
     &H14015C4FL, &H63066CD9L, &HFA0F3D63L, &H8D080DF5L,
     &H3B6E20C8L, &H4C69105EL, &HD56041E4L, &HA2677172L,
     &H3C03E4D1L, &H4B04D447L, &HD20D85FDL, &HA50AB56BL,
     &H35B5A8FAL, &H42B2986CL, &HDBBBC9D6L, &HACBCF940L,
     &H32D86CE3L, &H45DF5C75L, &HDCD60DCFL, &HABD13D59L,
     &H26D930ACL, &H51DE003AL, &HC8D75180L, &HBFD06116L,
     &H21B4F4B5L, &H56B3C423L, &HCFBA9599L, &HB8BDA50FL,
     &H2802B89EL, &H5F058808L, &HC60CD9B2L, &HB10BE924L,
     &H2F6F7C87L, &H58684C11L, &HC1611DABL, &HB6662D3DL,
     &H76DC4190L, &H1DB7106L, &H98D220BCL, &HEFD5102AL,
     &H71B18589L, &H6B6B51FL, &H9FBFE4A5L, &HE8B8D433L,
     &H7807C9A2L, &HF00F934L, &H9609A88EL, &HE10E9818L,
     &H7F6A0DBBL, &H86D3D2DL, &H91646C97L, &HE6635C01L,
     &H6B6B51F4L, &H1C6C6162L, &H856530D8L, &HF262004EL,
     &H6C0695EDL, &H1B01A57BL, &H8208F4C1L, &HF50FC457L,
     &H65B0D9C6L, &H12B7E950L, &H8BBEB8EAL, &HFCB9887CL,
     &H62DD1DDFL, &H15DA2D49L, &H8CD37CF3L, &HFBD44C65L,
     &H4DB26158L, &H3AB551CEL, &HA3BC0074L, &HD4BB30E2L,
     &H4ADFA541L, &H3DD895D7L, &HA4D1C46DL, &HD3D6F4FBL,
     &H4369E96AL, &H346ED9FCL, &HAD678846L, &HDA60B8D0L,
     &H44042D73L, &H33031DE5L, &HAA0A4C5FL, &HDD0D7CC9L,
     &H5005713CL, &H270241AAL, &HBE0B1010L, &HC90C2086L,
     &H5768B525L, &H206F85B3L, &HB966D409L, &HCE61E49FL,
     &H5EDEF90EL, &H29D9C998L, &HB0D09822L, &HC7D7A8B4L,
     &H59B33D17L, &H2EB40D81L, &HB7BD5C3BL, &HC0BA6CADL,
     &HEDB88320L, &H9ABFB3B6L, &H3B6E20CL, &H74B1D29AL,
     &HEAD54739L, &H9DD277AFL, &H4DB2615L, &H73DC1683L,
     &HE3630B12L, &H94643B84L, &HD6D6A3EL, &H7A6A5AA8L,
     &HE40ECF0BL, &H9309FF9DL, &HA00AE27L, &H7D079EB1L,
     &HF00F9344L, &H8708A3D2L, &H1E01F268L, &H6906C2FEL,
     &HF762575DL, &H806567CBL, &H196C3671L, &H6E6B06E7L,
     &HFED41B76L, &H89D32BE0L, &H10DA7A5AL, &H67DD4ACCL,
     &HF9B9DF6FL, &H8EBEEFF9L, &H17B7BE43L, &H60B08ED5L,
     &HD6D6A3E8L, &HA1D1937EL, &H38D8C2C4L, &H4FDFF252L,
     &HD1BB67F1L, &HA6BC5767L, &H3FB506DDL, &H48B2364BL,
     &HD80D2BDAL, &HAF0A1B4CL, &H36034AF6L, &H41047A60L,
     &HDF60EFC3L, &HA867DF55L, &H316E8EEFL, &H4669BE79L,
     &HCB61B38CL, &HBC66831AL, &H256FD2A0L, &H5268E236L,
     &HCC0C7795L, &HBB0B4703L, &H220216B9L, &H5505262FL,
     &HC5BA3BBEL, &HB2BD0B28L, &H2BB45A92L, &H5CB36A04L,
     &HC2D7FFA7L, &HB5D0CF31L, &H2CD99E8BL, &H5BDEAE1DL,
     &H9B64C2B0L, &HEC63F226L, &H756AA39CL, &H26D930AL,
     &H9C0906A9L, &HEB0E363FL, &H72076785L, &H5005713L,
     &H95BF4A82L, &HE2B87A14L, &H7BB12BAEL, &HCB61B38L,
     &H92D28E9BL, &HE5D5BE0DL, &H7CDCEFB7L, &HBDBDF21L,
     &H86D3D2D4L, &HF1D4E242L, &H68DDB3F8L, &H1FDA836EL,
     &H81BE16CDL, &HF6B9265BL, &H6FB077E1L, &H18B74777L,
     &H88085AE6L, &HFF0F6A70L, &H66063BCAL, &H11010B5CL,
     &H8F659EFFL, &HF862AE69L, &H616BFFD3L, &H166CCF45L,
     &HA00AE278L, &HD70DD2EEL, &H4E048354L, &H3903B3C2L,
     &HA7672661L, &HD06016F7L, &H4969474DL, &H3E6E77DBL,
     &HAED16A4AL, &HD9D65ADCL, &H40DF0B66L, &H37D83BF0L,
     &HA9BCAE53L, &HDEBB9EC5L, &H47B2CF7FL, &H30B5FFE9L,
     &HBDBDF21CL, &HCABAC28AL, &H53B39330L, &H24B4A3A6L,
     &HBAD03605L, &HCDD70693L, &H54DE5729L, &H23D967BFL,
     &HB3667A2EL, &HC4614AB8L, &H5D681B02L, &H2A6F2B94L,
     &HB40BBE37L, &HC30C8EA1L, &H5A05DF1BL, &H2D02EF8DL
    }

    Public Shared Function Cal(buf() As Byte, size As UInteger) As UInteger
        Dim crc As Long = &HFFFFFFFFL
        For i As UInteger = 0 To size - 1
            crc = crc32tab((crc Xor buf(i)) And &HFF) Xor (crc >> 8)
        Next
        Return Convert.ToUInt32(crc Xor &HFFFFFFFFL)
    End Function

End Class
